/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2014 Armin Lunkeit
 *
 * This implementation is part of math-core
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
//
//  matrix.cpp
//  math-core
//
//  Created by Armin Lunkeit on 15.02.14.
//  Copyright (c) 2014 Armin Lunkeit. All rights reserved.
//

#include "matrix.h"
#include <iostream>


std::ostream& operator<<(std::ostream& lhs, Matrix const& rhs)
{
    std::cout << std::endl;
    
    for( uint32_t i = 0; i < rhs.row(); i++ )
    {
		for (uint32_t x = 0; x < rhs.columns(); x++)
			std::cout << *(rhs._fmatrix[i][x]) << " ";

		std::cout << std::endl;
    }
    
    return lhs;
}

Matrix::Matrix( uint32_t n, uint32_t m )
: _n( n ),
  _m( m )
{
  _fmatrix.resize(_n);
    
  
	for (FMatrix::iterator iter = _fmatrix.begin(); iter != _fmatrix.end(); iter++)
		iter->resize( _m );

	for (FMatrix::iterator iter = _fmatrix.begin(); iter != _fmatrix.end(); iter++)
		for (PFArray::iterator iiter = iter->begin(); iiter != iter->end(); iiter++)
			*iiter = std::make_shared<Fraction>();
}

Matrix::Matrix( Matrix const& orig )
{
    this->_m = orig._m;
    this->_n = orig._n;
    
  _fmatrix = orig._fmatrix;

}

Matrix::~Matrix()
{
}

PFArray& Matrix::operator[](uint32_t const& index)
{
    return _fmatrix[ index ];
}

void Matrix::transpose()
{
	FMatrix _nmatrix;
	_nmatrix.resize(_m);

	for (FMatrix::iterator iter = _nmatrix.begin(); iter != _nmatrix.end(); iter++)
		iter->resize(_n);


	for (uint32_t i = 0; i < _n; i++)
		for (uint32_t j = 0; j < _m; j++)
			_nmatrix[j][i] = _fmatrix[i][j];

	_fmatrix = _nmatrix;

	uint32_t tn = _m;
	uint32_t tm = _n;

	_m = tm;
	_n = tn;
}

void Matrix::multiplyScalar( uint32_t s )
{
    for( uint32_t i = 0; i < _n; i++ )
        for( uint32_t j = 0; j < _m; j++ )
            *(_fmatrix[ i ][ j ]) = s * (*_fmatrix[ i ][ j ]);
}

Matrix Matrix::getColumn( uint32_t col ) const throw( std::runtime_error)
{
	if (col >= _m)
		throw std::runtime_error( "column index is greater than available index" );
    
    Matrix res( _n, 1 );
    
    for( uint32_t i = 0; i < _n; i++ )
    {
        res[ i ][ 0 ] = _fmatrix[ i ][ col ];
    }
    
    return res;
}

Matrix Matrix::getRow( uint32_t row ) const throw (std::runtime_error)
{
    if( row >= _n )
		throw std::runtime_error("column index is greater than available index");
    
    Matrix res( 1, _m );
    
	res._fmatrix[0] = _fmatrix[row];
    
    return res;
}

Matrix Matrix::operator * ( Matrix const& m ) const
{
    Matrix result( _n, m._m );
    
    // Counter over the column count of Matrix m2
    for( uint32_t mj = 0; mj < m.columns(); mj++ )
    {
        // iterate through the first row and multiply the values with
        // the values of the first column of m2
        for( uint32_t i = 0; i < _n; i++ )
        {
            Fraction sum;
            
            for( uint32_t j = 0; j < columns(); j++ )
            {
                sum += (*_fmatrix[ i ][ j ]) * (*m._fmatrix[ j][ mj ]);
                
                *(result[ i ][ mj ]) = sum;
            }
        }
    }
    
    return result;
}

